<?php
//消费券及领取操作

namespace App\Repositories;

use App\Models\FollowModel;
use App\Models\FollowRatePerDateModel;
use App\Models\MonitorModel;
use App\Models\RateDurationModel;
use http\Env\Request;
use App\Mail\Mail as Email;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\DB;


class FollowRepository extends BaseRepository
{

    private $tengxunApiUrl = '';

    public function __construct()
    {
        $this->tengxunApiUrl = env('TENGXUN_API_URL');
    }

    /**
     * 配置搜索条件
     * @param $params
     * @return array[]
     */
    public function getFilter ($params) {
        $user = request()->user();
        $where = [];
        $where[] = [
            'admin_id', $user['id']
        ];
        if ($params['level']) {
            $where[] = [
                'level', $params['level']
            ];
        }
        if ($params['strategy_id']) {
            $where[] = [
                'strategy_id', $params['strategy_id']
            ];
        }
        if ($params['be_monitor']) {
            $where[] = [
                'be_monitor', $params['be_monitor']
            ];
        }
        if ($params['stock_code']) {
            $where[] = [
                'stock_code', 'like', '%' . $params['stock_code'] . '%'
            ];
        }
        if ($params['stock_name']) {
            $where[] = [
                'stock_name', 'like', '%' . $params['stock_name'] . '%'
            ];
        }
        return [
            'where' => $where,
        ];
        /*return [
            'where' => [
                [
                    'stock_code', 'like', '%' . $params['stock_code'] . '%'
                ],
                [
                    'stock_name', 'like', '%' . $params['stock_name'] . '%'
                ],
                [
                    'level', $params['level']
                ]
            ],
        ];*/
    }

    /**
     * 获取列表
     * @param $params
     * @return array
     */
    public function getList ($params)
    {
        $filter = $this->getFilter($params);
        $orders = $this->_getOrders($params);
        $list = $this->getListItems(FollowModel::query(), $filter, $params['page_size'] ?? 20, ['*'], 'page', $orders);
        return $this->_updatePrice($list);;
    }

    /**
     * @param $list
     * @return mixed
     */
    private function _updatePrice($list)
    {
        $stockInfos = $this->_getListStockInfo($list['list']);
        $time = date('Y-m-d h:i:s');
        foreach ($stockInfos as $stockInfo) {
            foreach ($list['list'] as $follow) {
                if (empty($follow['unfollow_at']) && $follow['code_prefix'] . $follow['stock_code'] == $stockInfo['code_prefix'] . $stockInfo['stock_code']) {
                    $follow->updated_at = $time;
                    $follow->current_price_update_time = $time;
                    $follow->current_price = $stockInfo['current_price'];
                    $follow->rate = $this->_getRate($follow->current_price, $follow['follow_price']);
                    $follow->save();
                }
            }
        }
        return $list;
    }

    /**
     * @param $price1
     * @param $price2
     * @return float|int
     */
    private function _getRate ($price1, $price2)
    {
        return number_format(($price1 - $price2) / $price2, 4);
    }

    /**
     * @param $list
     * @return array
     */
    private function _getListStockInfo($list, $apiPrefix = 's_')
    {
        $url = $this->_getApiUrlByList($list, $apiPrefix);
        $stockTextInfo = $this->_getStockTextInfosByUrl($url);
        $stockInfos = $this->_getStockInfo($stockTextInfo);
        return $stockInfos;
    }

    /**
     * @param $response
     * @return array
     */
    public function _getStockInfo ($response)
    {
        $responseTextInfos = $this->_getArrayByStockFullText($response);
        $stockInfos = [];
        foreach ($responseTextInfos as $one) {
            $prefixAndCode = $this->_getStockPrefixAndCodeBySimpleText($one);
            $stockInfo = $this->_formatStockSimpleInfo($one, $prefixAndCode['prefix']);
            if ($stockInfo) {
                $stockInfos[] = $stockInfo;
            }
        }
        return $stockInfos;
    }

    /**
     * @param $response
     * @return string[]
     */
    public function _getArrayByStockFullText ($response)
    {
        $response = str_replace("\n", "", $response);
        return explode(';', $response);
    }

    /**
     * @param $list
     * @param $apiPrefix
     * @return string
     */
    public function _getApiUrlByList ($list, $apiPrefix)
    {
        $stockNum = '';
        foreach ($list as $one) {
            if (!str_contains($stockNum, $one['code_prefix'] . $one['stock_code'])) {
                $stockNum .= $apiPrefix . $one['code_prefix'] . $one['stock_code'] . ',';
            }
        }
        $stockNum = trim($stockNum, ',');
        return $this->tengxunApiUrl . 'q=' . $stockNum;
    }

    /**
     * @param $text
     * @param string $replace
     * @return array
     */
    private function _getStockPrefixAndCodeBySimpleText($text, $replace = 'v_s_')
    {
        $text = str_replace([$replace, '\n'], '', $text);
        $textArray = explode('=', $text);
        $stockPrefix = substr($textArray[0], 0, 2);
        $stockCode = substr($textArray[0], 2, 6);
        return [
            'prefix' => $stockPrefix,
            'code' => $stockCode
        ];
    }

    /**
     * @param $params
     * @return mixed
     */
    public function save ($params)
    {
        return $this->saveOrUpdate('FollowModel', $params);
    }

    /**
     * 根据代码获取详细信息
     * @param $code
     * @return string
     */
    public function getStockInfoByCode ($code)
    {
        $prefix = $this->getStockPrefixByCode($code);
        $url = $this->tengxunApiUrl . 'q=s_' . $prefix . $code;
        $response = $this->_getStockTextInfosByUrl($url);
        $data = $this->_formatStockSimpleInfo($response, $prefix);
        return $data;
    }

    /**
     * @param $url
     * @return array|false|string|string[]|null
     */
    public function _getStockTextInfosByUrl($url)
    {
        $client = new \GuzzleHttp\Client();
        $result = $client->get($url);
        $response = $result->getBody()->getContents();
        return mb_convert_encoding($response, 'UTF-8', 'GBK');
    }


    /**
     * @param $stockInfo
     * @return mixed
     */
    public function _formatStockSimpleInfo ($stockTextInfo, $prefix)
    {
        $stockInfos = $this->_getArrayByStockTextInfo($stockTextInfo);
        if ($stockInfos && count($stockInfos) > 1) {
            return [
                'code_prefix' => $prefix,
                'stock_name' => $stockInfos[1],
                'stock_code' => $stockInfos[2],
                'current_price' => $stockInfos[3],
            ];
        }
        return false;
    }

    /**
     * @param $stockTextInfo
     * @param null $keys
     * @return array|false|string[]
     */
    private function _getArrayByStockTextInfo ($stockTextInfo, $keys = null)
    {
        if ($stockTextInfo) {
            $stockInfos = explode('~', $stockTextInfo);
            if (is_array($keys)) {
                $stockData = [];
                foreach ($stockInfos as $key => $stockInfo) {
                    if (in_array($key, $keys)) {
                        $stockData[$key] = $stockInfo;
                    }
                }
                return $stockData;
            }
            return $stockInfos;
        }
        return false;
    }

    /**
     * @param $code
     * @return false|string
     */
    public function getStockPrefixByCode ($code)
    {
        // 暂时只支持深圳和上海
        $towLetter = substr($code, 0, 2);
        if ($towLetter == '60') {
            return 'sh';
        } else if ($towLetter == '00') {
            return 'sz';
        }
        return false;
    }

    /**
     * @param $id
     * @return false
     */
    public function getFollowInfo ($id)
    {
        $user = request()->user();
        if (empty($id)) {
            return false;
        }
        $follow = FollowModel::where('id', $id)
            ->where('admin_id', $user['id'])
            ->select('id', 'strategy_id', 'code_prefix', 'stock_code', 'stock_name', 'follow_at', 'follow_price', 'current_price', 'current_price_update_time', 'unfollow_at', 'monitor_id', 'unfollow_price', 'be_monitor', 'level', 'rate', 'highest_price', 'lowest_price', 'highest_rate', 'lowest_rate', 'summary')
            ->first();
        if ($follow['strategy']) {
            $follow['strategy_name'] = $follow['strategy']['name'];
            unset($follow['strategy']);
        }
        if ($follow['level']) {
            $follow['level_name'] = FollowModel::LEVELS[$follow['level']];
        }
        $follow['be_monitor_str'] = FollowModel::MONITORS[$follow['be_monitor']];
        return $follow;
    }

    /**
     *更新最大最小价格
     * 多的话有问题，可能需要while(true)循环
     */
    public function updateHightestAndLowestPrice ()
    {
        $rateDurationRepo = new RateDurationRepository();
        $follows = FollowModel::whereNull('unfollow_at')
            ->select('id', 'code_prefix', 'follow_price', 'follow_at', 'stock_code', 'highest_price', 'follow_at', 'lowest_price')
            ->get();
        $rateDurations = RateDurationModel::where('is_following', RateDurationModel::FOLLOWING)
            ->select('follow_id', DB::raw('MAX(rate) as max_rate'))
            ->orderBy('rate', 'desc')
            ->groupBy('follow_id')
            ->pluck('max_rate', 'follow_id');
        $addRateDurationDatas = [];
        $url = $this->_getApiUrlByList($follows, '');
        $stockTextInfo = $this->_getStockTextInfosByUrl($url);
        $stockTextInfos = $this->_getArrayByStockFullText($stockTextInfo);
        $stockTextInfos = array_filter($stockTextInfos);
        foreach ($stockTextInfos as $stockTextInfo) {
            $prefixAndCode = $this->_getStockPrefixAndCodeBySimpleText($stockTextInfo, 'v_');
            foreach ($follows as $follow) {
                if ($prefixAndCode['prefix'] . $prefixAndCode['code'] == $follow['code_prefix'] . $follow['stock_code']) {
                    $stockInfos = $this->_getArrayByStockTextInfo($stockTextInfo, [33, 34]);
                    $followRatePerDate = FollowRatePerDateModel::where('created_date', date('Y-m-d'))
                        ->where('follow_id', $follow['id'])
                        ->first();
//                    dd($followRatePerDate);
                    if (empty($followRatePerDate)) {
                        $followRatePerDate = new FollowRatePerDateModel();
                        $followRatePerDate->follow_id = $follow['id'];
                        $followRatePerDate->stock_code =$follow['stock_code'];
                        $followRatePerDate->stock_name =$follow['stock_name'];
                        $followRatePerDate->created_date = date('Y-m-d');
                        $followRatePerDate->created_at = date('Y-m-d H:i:s');
                    }
                    // 关注日期不等于当天
                    if (substr($follow['follow_at'], 0, 10) != date('Y-m-d')) {
                        // 如果当前最高价大于之前最高价
                        if ($stockInfos[33] >= $follow['highest_price']) {
                            $operateRepo = new OperateRepository();
                            $followRatePerDate->duration = $follow->highest_rate_duration = $operateRepo->getRealDuration($follow['follow_at'], date('Y-m-d'));
                            $followRatePerDate->highest_price = $follow->highest_price = $stockInfos[33];
                            $followRatePerDate->highest_rate = $follow->highest_rate = $this->_getRate($follow->highest_price, $follow->follow_price);
                            // 如果涨幅大于rate_duration表的最大涨幅
                            $addRateDurationData = $this->_getAddRateDurationData($rateDurations, $follow);
                            if (count($addRateDurationData) > 0) {
                                $addRateDurationDatas = array_merge($addRateDurationDatas, $addRateDurationData);
                            }
                        }
                        if ((empty($follow['lowest_price']) || $stockInfos[34] < $follow['lowest_price'])) {
                            // 如果当前最低价小于之前最低价或最低价为空
                            $followRatePerDate->lowest_price = $follow->lowest_price = $stockInfos[34];
                            $followRatePerDate->lowest_rate = $follow->lowest_rate = $this->_getRate($follow->lowest_price, $follow['follow_price']);
                        }
                    }

//                    dd($followRatePerDate);

                    $follow->updated_at = date('Y-m-d h:i:s');
                    $follow->save();
//                    dd($followRatePerDate, $follow);
                    $followRatePerDate->save();
                }
            }
        }
        $rateDurationRepo->add($addRateDurationDatas);
    }

    /**
     * @param $rateDurations
     * @param $follow
     * @return array
     */
    private function _getAddRateDurationData ($rateDurations, $follow)
    {
        $addRateDurations = [];
        $fromRate = RateDurationModel::RATES[0];
        $toRate = RateDurationModel::RATES[1];
        // 如果涨幅大于rate_duration表的最大涨幅
        if (
            !empty($rateDurations[$follow->id])                         // 如果rate_durations存在
            && $follow->highest_rate > $rateDurations[$follow->id]      // 如果当前涨幅大于表中的最大涨幅
        ) {
            for ($rate = $fromRate; $rate <= $toRate; $rate += 5) {
                if ($rate > $rateDurations[$follow->id] && $rate < $follow->highest_rate) {
                    $now = date('Y-m-d H:i:s');
                    $addRateDurations[] = [
                        'rate' => $rate,
                        'real_rate' => $follow->highest_rate,
                        'follow_id' => $follow->id,
                        'admin_id' => 1,
                        'duration' => $follow->highest_rate_duration,
                        'created_at' => $now,
                        'updated_at' => $now,
                    ];
                }
            }
        }
        return $addRateDurations;
    }

    /**
     * 获取时间相隔多少分钟
     * @param $triggerTime
     * @return float|int
     */
    private function _getDiffMinutes ($triggerTime)
    {
        $now = strtotime(date('H:i:s'));
        $triggerTime = strtotime($triggerTime);
        $diffMinutes = abs(($now - $triggerTime) / 60);
        return $diffMinutes;
    }

    /**
     * @param false $debug
     */
    public function monitor ($debug = false)
    {
        /*$follows = FollowModel::select('id', 'code_prefix', 'follow_price', 'stock_code')
            ->where('be_monitor', FollowModel::IS_MONITOR)
            ->get();*/
        $opening = isOpening();
//        $opening = true;
        if ($opening) {
            $monitors = MonitorModel::where('status', MonitorModel::ENABLED)
                ->select('id', 'stock_code', 'code_prefix', 'trigger_lowest_price', 'trigger_highest_price', 'ready_trigger_times', 'trigger_times_per_day', 'trigger_date', 'trigger_time', 'admin_id')
                ->get();
            if ($monitors) {
                $url = $this->_getApiUrlByList($monitors, 's_');
                $stockTextInfo = $this->_getStockTextInfosByUrl($url);
                $responseTextInfos = $this->_getArrayByStockFullText($stockTextInfo);
                $stockInfos = [];
                foreach ($responseTextInfos as $one) {
                    if ($one) {
                        $prefixAndCode = $this->_getStockPrefixAndCodeBySimpleText($one);
                        $prices = $this->_getArrayByStockTextInfo($one, [3]);
                        $stockInfos[$prefixAndCode['code']] = $prices[3];
                    }
                }
                $monitorAdmins = [];
                foreach ($monitors as $monitor) {
                    // 检查是否达到发送监听的最大值
                    if ($monitor['trigger_date'] == date('Y-m-d')) {
                        // 如果当天发送测试小于设置的每天可发送次数 && 监听时间间隔大于设置的时间间隔
                        $diffMinutes = $this->_getDiffMinutes($monitor['trigger_time']);
                        if ($monitor['ready_trigger_times'] < $monitor['trigger_times_per_day'] && $diffMinutes >= $monitor['monitor_every_minutes']) {
                            // 如果是监听的股票代码
                            $currentPrice = $stockInfos[$monitor['stock_code']] * 100;
                            $monitorLowestPrice = $monitor['trigger_lowest_price'] * 100;
                            $monitorHighestPrice = $monitor['trigger_highest_price'] * 100;
                            $trigger = false;
                            if ($currentPrice < $monitorLowestPrice) {                // 触发最低值
                                $msg = '触发最低值';
                                $trigger = true;
                                $type = 'lowest';
                                $triggerPrice = $monitorLowestPrice;
                            } else if ($currentPrice > $monitorHighestPrice) {        // 触发最高值
                                $msg = '触发最高值';
                                $trigger = true;
                                $type = 'highest';
                                $triggerPrice = $monitorHighestPrice;
                            }
                            if ($trigger) {
                                $monitor->ready_trigger_times = $monitor['ready_trigger_times'] + 1;
                                $monitor->trigger_date = date('Y-m-d');
                                $monitor->save();
                                // 写入需要发送消息的人列表
                                $monitorAdmins[$monitor['admin_id']][] = [
                                    'msg' => $msg,
                                    'current_price' => $currentPrice,
                                    'type' => $type,
                                    'trigger_price' => $triggerPrice,
                                    'stock_code' => $monitor['stock_code']
                                ];
                            }
                        }
                    } else {
                        $monitor->trigger_date = date('Y-m-d');
                        $monitor->ready_trigger_times = 0;
                        $monitor->save();
                    }
                }
                if ($monitorAdmins) {
//                    $sendContent = $this->_formatEmailContent($monitorAdmins);
                    // 发送消息
                    try {
                        $email = 'billlzb@qq.com';
                        $params['view'] = 'monitor';
                        $params['subject'] = '阙值提醒';
                        $params['content'] = $monitorAdmins[1];
                        Mail::to($email)->send(new Email($params));
                        // 邮件发送成功，可以执行其他操作
                    } catch (SendException $e) {
                        // 捕获发送邮件异常，并打印错误信息
                        Log::info('CallBackLog:' . json_encode($e->getMessage()));
                    }
                }
            }
        }
    }

    /**
     * @param $monitorAdmins
     */
    /*private function _formatEmailContent ($monitorAdmins)
    {
        $contents = [];
        if ($monitorAdmins) {
            foreach ($monitorAdmins as $monitorAdmin) {
//                $contents
            }
        }
    }*/

    /**
     * @param $followId
     * @return bool
     */
    public function updateBeMonitor ($followId, $monitorId)
    {
        $follow = FollowModel::find($followId);
        $follow->be_monitor = 1;
        $follow->monitor_id = $monitorId;
        $follow->save();
    }
}
